#ifndef _smart_ptr_for_object_c_
#define _smart_ptr_for_object_c_

#include <boost/type_traits.hpp>
#include "CCObject.h"


template<bool>
struct objectc_or_cpp_dispatcher {
	template <typename T> 
	inline static void release(T *ptr) {
		if(ptr) ptr->release();
	}
	template <typename T> 
	inline static void retain(T *ptr) {
		if(ptr) ptr->retain();
	}
};

#ifdef __OBJC__
//if is object c type
template<>
struct objectc_or_cpp_dispatcher<false>{
	template <typename T> 
	inline static void release(T *ptr) {
		if (ptr) [ptr release];
	}
};

#endif

template<class T> class scoped_object_c_ptr // noncopyable
	{
	public:
//		inline static T *allocDefaultPtr() {
//			return [[T alloc] init];
//		}
	private:
		
		T * ptr;
		
		explicit scoped_object_c_ptr(scoped_object_c_ptr const &);
		scoped_object_c_ptr & operator=(scoped_object_c_ptr const &);
		
		typedef scoped_object_c_ptr<T> this_type;
		
		void operator==( scoped_object_c_ptr const& ) const;
		void operator!=( scoped_object_c_ptr const& ) const;
		
	public:
		
		typedef T element_type;
		
		explicit scoped_object_c_ptr(T * p = 0): ptr(p) // never throws
		{
		}
		explicit scoped_object_c_ptr(T * p, bool is_auto_release): ptr(p)
		{
			BOOST_ASSERT(is_auto_release);
			objectc_or_cpp_dispatcher<boost::is_base_of<cocos2d::CCObject, T>::value>::retain(ptr);
		}

		
		~scoped_object_c_ptr() // never throws
		{
			// intentionally complex - simplification causes regressions
			typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];
			(void) sizeof(type_must_be_complete);
			//I am very good
			objectc_or_cpp_dispatcher<boost::is_base_of<cocos2d::CCObject, T>::value>::release(ptr);
		}
		
		void reset(T * p = 0) // never throws
		{
			BOOST_ASSERT(p == 0 || p != ptr); // catch self-reset errors
			this_type(p).swap(*this);
		}
		
		operator T*() const
		{
			BOOST_ASSERT(ptr != 0);
			return ptr;
		}
		
		T * operator->() const // never throws
		{
			BOOST_ASSERT( ptr != 0 );
			return ptr;
		}
		
		T & operator*() const // never throws
		{
			BOOST_ASSERT(ptr != 0);
			return *ptr;
		}
		
		
		T * get() const // never throws
		{
			return ptr;
		}
		
		void swap(scoped_object_c_ptr & b) // never throws
		{
			T * tmp = b.ptr;
			b.ptr = ptr;
			ptr = tmp;
		}
	};

template<class T> inline void swap(scoped_object_c_ptr<T> & a, scoped_object_c_ptr<T> & b) // never throws
{
    a.swap(b);
}

// get_pointer(p) is a generic way to say p.get()

template<class T> inline T * get_pointer(scoped_object_c_ptr<T> const & p)
{
    return p.get();
}

#endif